#include "stdafx.h"
#include "net/IPAddressImpl.hpp"
#include "charset/byteorder.hpp"
using JHCPP::charset::CByteOrder;
#include "utils/numberformatter.hpp"
using JHCPP::utils::CNumberFormatter;
#include "stdlib/bugcheck.hpp"
using namespace JHCPP::stdlib;
#include "charset/string.hpp"
using JHCPP::charset::toLower;

NET_NAMESPACE_BEGIN

template <typename T>
unsigned maskBits(T val, unsigned size)
{
	unsigned count = 0;
	if (val)
	{
		val = (val ^ (val - 1)) >> 1;
		for (count = 0; val; ++count) val >>= 1;
	}
	else count = size;
	return size - count;
}

//
// CIPAddressImpl
//
CIPAddressImpl::CIPAddressImpl()
{
}

CIPAddressImpl::~CIPAddressImpl()
{
}


//
// CIPv4AddressImpl
//
CIPv4AddressImpl::CIPv4AddressImpl()
{
	std::memset(&m_addr, 0, sizeof(m_addr));
}

CIPv4AddressImpl::CIPv4AddressImpl(const void* addr)
{
	std::memcpy(&m_addr, addr, sizeof(m_addr));
}

CIPv4AddressImpl::CIPv4AddressImpl(unsigned prefix)
{
	UInt32 addr = (prefix == 32) ? 0xffffffff : ~(0xffffffff >> prefix);
	m_addr.s_addr = CByteOrder::toNetwork(addr);
}

CIPv4AddressImpl::CIPv4AddressImpl(const CIPv4AddressImpl& addr)
{
	std::memcpy(&m_addr, &addr.m_addr, sizeof(m_addr));
}

CIPv4AddressImpl& CIPv4AddressImpl::operator = (const CIPv4AddressImpl& addr)
{
	if (this == &addr)
		return *this;

	std::memcpy(&m_addr, &addr.m_addr, sizeof(m_addr));
	return *this;
}

std::string CIPv4AddressImpl::toString() const
{
	const UInt8* bytes = reinterpret_cast<const UInt8*>(&m_addr);
	std::string result;
	result.reserve(16);
	CNumberFormatter::append(result, bytes[0]);
	result.append(".");
	CNumberFormatter::append(result, bytes[1]);
	result.append(".");
	CNumberFormatter::append(result, bytes[2]);
	result.append(".");
	CNumberFormatter::append(result, bytes[3]);
	return result;
}

JH_SOCKETLEN CIPv4AddressImpl::length() const
{
	return sizeof(m_addr);
}

const void* CIPv4AddressImpl::addr() const
{
	return &m_addr;
}

CIPAddressImpl::Family CIPv4AddressImpl::family() const
{
	return CIPAddressImpl::IPv4;
}

int CIPv4AddressImpl::af() const
{
	return AF_INET;
}

unsigned CIPv4AddressImpl::prefixLength() const
{
	return maskBits(ntohl(m_addr.s_addr), 32);
}

UInt32 CIPv4AddressImpl::scope() const
{
	return 0;
}

bool CIPv4AddressImpl::isWildcard() const
{
	return m_addr.s_addr == INADDR_ANY;
}

bool CIPv4AddressImpl::isBroadcast() const
{
	return m_addr.s_addr == INADDR_NONE;
}

bool CIPv4AddressImpl::isLoopback() const
{
	return (ntohl(m_addr.s_addr) & 0xFF000000) == 0x7F000000; // 127.0.0.1 to 127.255.255.255
}

bool CIPv4AddressImpl::isMulticast() const
{
	return (ntohl(m_addr.s_addr) & 0xF0000000) == 0xE0000000; // 224.0.0.0/24 to 239.0.0.0/24
}

bool CIPv4AddressImpl::isLinkLocal() const
{
	return (ntohl(m_addr.s_addr) & 0xFFFF0000) == 0xA9FE0000; // 169.254.0.0/16
}

bool CIPv4AddressImpl::isSiteLocal() const
{
	UInt32 addr = ntohl(m_addr.s_addr);
	return (addr & 0xFF000000) == 0x0A000000 ||     // 10.0.0.0/24
		(addr & 0xFFFF0000) == 0xC0A80000 ||        // 192.68.0.0/16
		(addr >= 0xAC100000 && addr <= 0xAC1FFFFF); // 172.16.0.0 to 172.31.255.255
}

bool CIPv4AddressImpl::isIPv4Compatible() const
{
	return true;
}

bool CIPv4AddressImpl::isIPv4Mapped() const
{
	return true;
}

bool CIPv4AddressImpl::isWellKnownMC() const
{
	return (ntohl(m_addr.s_addr) & 0xFFFFFF00) == 0xE0000000; // 224.0.0.0/8
}

bool CIPv4AddressImpl::isNodeLocalMC() const
{
	return false;
}

bool CIPv4AddressImpl::isLinkLocalMC() const
{
	return (ntohl(m_addr.s_addr) & 0xFF000000) == 0xE0000000; // 244.0.0.0/24
}

bool CIPv4AddressImpl::isSiteLocalMC() const
{
	return (ntohl(m_addr.s_addr) & 0xFFFF0000) == 0xEFFF0000; // 239.255.0.0/16
}

bool CIPv4AddressImpl::isOrgLocalMC() const
{
	return (ntohl(m_addr.s_addr) & 0xFFFF0000) == 0xEFC00000; // 239.192.0.0/16
}

bool CIPv4AddressImpl::isGlobalMC() const
{
	UInt32 addr = ntohl(m_addr.s_addr);
	return addr >= 0xE0000100 && addr <= 0xEE000000; // 224.0.1.0 to 238.255.255.255
}

CIPv4AddressImpl CIPv4AddressImpl::parse(const std::string& addr)
{
	if (addr.empty()) 
		return CIPv4AddressImpl();

#if defined(_WIN32) 
	struct in_addr ia;
	ia.s_addr = inet_addr(addr.c_str());
	if (ia.s_addr == INADDR_NONE && addr != "255.255.255.255")
		return CIPv4AddressImpl();
	else
		return CIPv4AddressImpl(&ia);
#else
	struct in_addr ia;
	if (inet_aton(addr.c_str(), &ia))
		return CIPv4AddressImpl(&ia);
	else
		return CIPv4AddressImpl();
#endif
}

void CIPv4AddressImpl::mask(const CIPAddressImpl* pMask, const CIPAddressImpl* pSet)
{
	jh_assert(pMask->af() == AF_INET && pSet->af() == AF_INET);

	m_addr.s_addr &= static_cast<const CIPv4AddressImpl*>(pMask)->m_addr.s_addr;
	m_addr.s_addr |= static_cast<const CIPv4AddressImpl*>(pSet)->m_addr.s_addr & ~static_cast<const CIPv4AddressImpl*>(pMask)->m_addr.s_addr;
}

CIPAddressImpl* CIPv4AddressImpl::clone() const
{
	return new CIPv4AddressImpl(&m_addr);
}

CIPv4AddressImpl CIPv4AddressImpl::operator & (const CIPv4AddressImpl& addr) const
{
	CIPv4AddressImpl result(&m_addr);
	result.m_addr.s_addr &= addr.m_addr.s_addr;
	return result;
}

CIPv4AddressImpl CIPv4AddressImpl::operator | (const CIPv4AddressImpl& addr) const
{
	CIPv4AddressImpl result(&m_addr);
	result.m_addr.s_addr |= addr.m_addr.s_addr;
	return result;
}

CIPv4AddressImpl CIPv4AddressImpl::operator ^ (const CIPv4AddressImpl& addr) const
{
	CIPv4AddressImpl result(&m_addr);
	result.m_addr.s_addr ^= addr.m_addr.s_addr;
	return result;
}

CIPv4AddressImpl CIPv4AddressImpl::operator ~ () const
{
	CIPv4AddressImpl result(&m_addr);
	result.m_addr.s_addr ^= 0xffffffff;
	return result;
}

bool CIPv4AddressImpl::operator == (const CIPv4AddressImpl& addr) const
{
	return 0 == std::memcmp(&addr.m_addr, &m_addr, sizeof(m_addr));
}

bool CIPv4AddressImpl::operator != (const CIPv4AddressImpl& addr) const
{
	return !(*this == addr);
}



#ifdef JH_HAVE_IPv6
//
// CIPv6AddressImpl
//
CIPv6AddressImpl::CIPv6AddressImpl() : m_scope(0)
{
	std::memset(&m_addr, 0, sizeof(m_addr));
}

CIPv6AddressImpl::CIPv6AddressImpl(const void* addr) : m_scope(0)
{
	std::memcpy(&m_addr, addr, sizeof(m_addr));
}

CIPv6AddressImpl::CIPv6AddressImpl(const void* addr, UInt32 scope) : m_scope(scope)
{
	std::memcpy(&m_addr, addr, sizeof(m_addr));
}

CIPv6AddressImpl::CIPv6AddressImpl(const CIPv6AddressImpl& addr) : m_scope(addr.m_scope)
{
	std::memcpy((void*) &m_addr, (void*) &addr.m_addr, sizeof(m_addr));
}

CIPv6AddressImpl& CIPv6AddressImpl::operator = (const CIPv6AddressImpl& addr)
{
	if (this == &addr)
		return *this;

	m_scope = addr.m_scope;
	std::memcpy(&m_addr, &addr.m_addr, sizeof(m_addr));
	return *this;
}

CIPv6AddressImpl::CIPv6AddressImpl(unsigned prefix) : m_scope(0)
{
	unsigned i = 0;
#ifdef JH_OS_WINDOWS
	for (; prefix >= 16; ++i, prefix -= 16) 
	{
		m_addr.s6_addr16[i] = 0xffff;
	}
	if (prefix > 0)
	{
		m_addr.s6_addr16[i++] = CByteOrder::toNetwork(static_cast<UInt16>(~(0xffff >> prefix)));
	}
	while (i < 8)
	{
		m_addr.s6_addr16[i++] = 0;
	}
#else
	for (; prefix >= 32; ++i, prefix -= 32) 
	{
		m_addr.s6_addr32[i] = 0xffffffff;
	}
	if (prefix > 0)
	{
		m_addr.s6_addr32[i++] = CByteOrder::toNetwork(~(0xffffffffU >> prefix));
	}
	while (i < 4)
	{
		m_addr.s6_addr32[i++] = 0;
	}
#endif
}

std::string CIPv6AddressImpl::toString() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	if ((isIPv4Compatible() && !isLoopback()) || isIPv4Mapped())
	{
		std::string result;
		result.reserve(24);
		if (words[5] == 0)
			result.append("::");
		else
			result.append("::ffff:");
		const UInt8* bytes = reinterpret_cast<const UInt8*>(&m_addr);
		CNumberFormatter::append(result, bytes[12]);
		result.append(".");
		CNumberFormatter::append(result, bytes[13]);
		result.append(".");
		CNumberFormatter::append(result, bytes[14]);
		result.append(".");
		CNumberFormatter::append(result, bytes[15]);
		return result;
	}
	else
	{
		std::string result;
		result.reserve(64);
		bool zeroSequence = false;
		int i = 0;
		while (i < 8)
		{
			if (!zeroSequence && words[i] == 0)
			{
				int zi = i;
				while (zi < 8 && words[zi] == 0) ++zi;
				if (zi > i + 1)
				{
					i = zi;
					result.append(":");
					zeroSequence = true;
				}
			}
			if (i > 0) result.append(":");
			if (i < 8) CNumberFormatter::appendHex(result, CByteOrder::fromNetwork(words[i++]));
		}
		if (m_scope > 0)
		{
			result.append("%");
		#if defined(_WIN32)
			CNumberFormatter::append(result, m_scope);
		#else
			char buffer[IFNAMSIZ];
			if (if_indextoname(m_scope, buffer))
			{
				result.append(buffer);
			}
			else
			{
				CNumberFormatter::append(result, _scope);
			}
		#endif
		}
		return toLower(result);
	}
}

JH_SOCKETLEN CIPv6AddressImpl::length() const
{
	return sizeof(m_addr);
}

const void* CIPv6AddressImpl::addr() const
{
	return &m_addr;
}

CIPAddressImpl::Family CIPv6AddressImpl::family() const
{
	return CIPAddressImpl::IPv6;
}

int CIPv6AddressImpl::af() const
{
	return AF_INET6;
}

unsigned CIPv6AddressImpl::prefixLength() const
{
	unsigned bits = 0;
	unsigned bitPos = 128;
#if defined(JH_OS_LINUX)
	for (int i = 3; i >= 0; --i)
	{
		unsigned addr = ntohl(m_addr.s6_addr32[i]);
		if ((bits = maskBits(addr, 32))) return (bitPos - (32 - bits));
		bitPos -= 32;
	}
	return 0;
#elif defined(JH_OS_WINDOWS)
	for (int i = 7; i >= 0; --i)
	{
		unsigned short addr = CByteOrder::fromNetwork(m_addr.s6_addr16[i]);
		if ((bits = maskBits(addr, 16))) return (bitPos - (16 - bits));
		bitPos -= 16;
	}
	return 0;
#else
	#warning prefixLength() not implemented
		throw NotImplementedException("prefixLength() not implemented");
#endif
}

UInt32 CIPv6AddressImpl::scope() const
{
	return m_scope;
}

bool CIPv6AddressImpl::isWildcard() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && 
		words[4] == 0 && words[5] == 0 && words[6] == 0 && words[7] == 0;
}

bool CIPv6AddressImpl::isBroadcast() const
{
	return false;
}

bool CIPv6AddressImpl::isLoopback() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && 
		words[4] == 0 && words[5] == 0 && words[6] == 0 && CByteOrder::fromNetwork(words[7]) == 0x0001;
}

bool CIPv6AddressImpl::isMulticast() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFE0) == 0xFF00;
}

bool CIPv6AddressImpl::isLinkLocal() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFE0) == 0xFE80;
}

bool CIPv6AddressImpl::isSiteLocal() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return ((CByteOrder::fromNetwork(words[0]) & 0xFFE0) == 0xFEC0) || ((CByteOrder::fromNetwork(words[0]) & 0xFF00) == 0xFC00);
}

bool CIPv6AddressImpl::isIPv4Compatible() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && words[4] == 0 && words[5] == 0;
}

bool CIPv6AddressImpl::isIPv4Mapped() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && words[4] == 0 && CByteOrder::fromNetwork(words[5]) == 0xFFFF;
}

bool CIPv6AddressImpl::isWellKnownMC() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFF0) == 0xFF00;
}

bool CIPv6AddressImpl::isNodeLocalMC() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFEF) == 0xFF01;
}

bool CIPv6AddressImpl::isLinkLocalMC() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFEF) == 0xFF02;
}

bool CIPv6AddressImpl::isSiteLocalMC() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFEF) == 0xFF05;
}

bool CIPv6AddressImpl::isOrgLocalMC() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFEF) == 0xFF08;
}

bool CIPv6AddressImpl::isGlobalMC() const
{
	const UInt16* words = reinterpret_cast<const UInt16*>(&m_addr);
	return (CByteOrder::fromNetwork(words[0]) & 0xFFEF) == 0xFF0F;
}

CIPv6AddressImpl CIPv6AddressImpl::parse(const std::string& addr)
{
	if (addr.empty()) 
		return CIPv6AddressImpl();
#if defined(_WIN32)
	struct addrinfo* pAI;
	struct addrinfo hints;
	std::memset(&hints, 0, sizeof(hints));
	hints.ai_flags = AI_NUMERICHOST;
	int rc = getaddrinfo(addr.c_str(), NULL, &hints, &pAI);
	if (rc == 0)
	{
		CIPv6AddressImpl result = CIPv6AddressImpl(&reinterpret_cast<struct sockaddr_in6*>(pAI->ai_addr)->sin6_addr, static_cast<int>(reinterpret_cast<struct sockaddr_in6*>(pAI->ai_addr)->sin6_scope_id));
		freeaddrinfo(pAI);
		return result;
	}
	else 
		return CIPv6AddressImpl();
#else
	struct in6_addr ia;
	std::string::size_type pos = addr.find('%');
	if (std::string::npos != pos)
	{
		std::string::size_type start = ('[' == addr[0]) ? 1 : 0;
		std::string unscopedAddr(addr, start, pos - start);
		std::string scope(addr, pos + 1, addr.size() - start - pos);
		UInt32 scopeId(0);
		if (!(scopeId = if_nametoindex(scope.c_str())))
			return CIPv6AddressImpl();
		if (inet_pton(AF_INET6, unscopedAddr.c_str(), &ia) == 1)
			return CIPv6AddressImpl(&ia, scopeId);
		else
			return CIPv6AddressImpl();
	}
	else
	{
		if (inet_pton(AF_INET6, addr.c_str(), &ia) == 1)
			return CIPv6AddressImpl(&ia);
		else
			return CIPv6AddressImpl();
	}
#endif
}

void CIPv6AddressImpl::mask(const CIPAddressImpl* pMask, const CIPAddressImpl* pSet)
{
	throw NotImplementedException("mask() is only supported for IPv4 addresses");
}

CIPAddressImpl* CIPv6AddressImpl::clone() const
{
	return new CIPv6AddressImpl(*this);
}

CIPv6AddressImpl CIPv6AddressImpl::operator & (const CIPv6AddressImpl& addr) const
{
	if (m_scope != addr.m_scope)
		throw InvalidArgumentException("Scope ID of passed IPv6 address does not match with the source one.");

	CIPv6AddressImpl result(*this);
#ifdef JH_OS_WINDOWS
	result.m_addr.s6_addr16[0] &= addr.m_addr.s6_addr16[0];
	result.m_addr.s6_addr16[1] &= addr.m_addr.s6_addr16[1];
	result.m_addr.s6_addr16[2] &= addr.m_addr.s6_addr16[2];
	result.m_addr.s6_addr16[3] &= addr.m_addr.s6_addr16[3];
	result.m_addr.s6_addr16[4] &= addr.m_addr.s6_addr16[4];
	result.m_addr.s6_addr16[5] &= addr.m_addr.s6_addr16[5];
	result.m_addr.s6_addr16[6] &= addr.m_addr.s6_addr16[6];
	result.m_addr.s6_addr16[7] &= addr.m_addr.s6_addr16[7];
#else
	result.m_addr.s6_addr32[0] &= addr.m_addr.s6_addr32[0];
	result.m_addr.s6_addr32[1] &= addr.m_addr.s6_addr32[1];
	result.m_addr.s6_addr32[2] &= addr.m_addr.s6_addr32[2];
	result.m_addr.s6_addr32[3] &= addr.m_addr.s6_addr32[3];
#endif
	return result;
}

CIPv6AddressImpl CIPv6AddressImpl::operator | (const CIPv6AddressImpl& addr) const
{
	if (m_scope != addr.m_scope) 
		throw InvalidArgumentException("Scope ID of passed IPv6 address does not match with the source one.");

	CIPv6AddressImpl result(*this);
#ifdef JH_OS_WINDOWS
	result.m_addr.s6_addr16[0] |= addr.m_addr.s6_addr16[0];
	result.m_addr.s6_addr16[1] |= addr.m_addr.s6_addr16[1];
	result.m_addr.s6_addr16[2] |= addr.m_addr.s6_addr16[2];
	result.m_addr.s6_addr16[3] |= addr.m_addr.s6_addr16[3];
	result.m_addr.s6_addr16[4] |= addr.m_addr.s6_addr16[4];
	result.m_addr.s6_addr16[5] |= addr.m_addr.s6_addr16[5];
	result.m_addr.s6_addr16[6] |= addr.m_addr.s6_addr16[6];
	result.m_addr.s6_addr16[7] |= addr.m_addr.s6_addr16[7];
#else
	result.m_addr.s6_addr32[0] |= addr.m_addr.s6_addr32[0];
	result.m_addr.s6_addr32[1] |= addr.m_addr.s6_addr32[1];
	result.m_addr.s6_addr32[2] |= addr.m_addr.s6_addr32[2];
	result.m_addr.s6_addr32[3] |= addr.m_addr.s6_addr32[3];
#endif
	return result;
}

CIPv6AddressImpl CIPv6AddressImpl::operator ^ (const CIPv6AddressImpl& addr) const
{
	if (m_scope != addr.m_scope)
		throw InvalidArgumentException("Scope ID of passed IPv6 address does not match  with the source one.");

	CIPv6AddressImpl result(*this);

#ifdef JH_OS_WINDOWS
	result.m_addr.s6_addr16[0] ^= addr.m_addr.s6_addr16[0];
	result.m_addr.s6_addr16[1] ^= addr.m_addr.s6_addr16[1];
	result.m_addr.s6_addr16[2] ^= addr.m_addr.s6_addr16[2];
	result.m_addr.s6_addr16[3] ^= addr.m_addr.s6_addr16[3];
	result.m_addr.s6_addr16[4] ^= addr.m_addr.s6_addr16[4];
	result.m_addr.s6_addr16[5] ^= addr.m_addr.s6_addr16[5];
	result.m_addr.s6_addr16[6] ^= addr.m_addr.s6_addr16[6];
	result.m_addr.s6_addr16[7] ^= addr.m_addr.s6_addr16[7];
#else
	result.m_addr.s6_addr32[0] ^= addr.m_addr.s6_addr32[0];
	result.m_addr.s6_addr32[1] ^= addr.m_addr.s6_addr32[1];
	result.m_addr.s6_addr32[2] ^= addr.m_addr.s6_addr32[2];
	result.m_addr.s6_addr32[3] ^= addr.m_addr.s6_addr32[3];
#endif
	return result;
}

CIPv6AddressImpl CIPv6AddressImpl::operator ~ () const
{
	CIPv6AddressImpl result(*this);
#ifdef JH_OS_WINDOWS
	result.m_addr.s6_addr16[0] ^= 0xffff;
	result.m_addr.s6_addr16[1] ^= 0xffff;
	result.m_addr.s6_addr16[2] ^= 0xffff;
	result.m_addr.s6_addr16[3] ^= 0xffff;
	result.m_addr.s6_addr16[4] ^= 0xffff;
	result.m_addr.s6_addr16[5] ^= 0xffff;
	result.m_addr.s6_addr16[6] ^= 0xffff;
	result.m_addr.s6_addr16[7] ^= 0xffff;
#else
	result.m_addr.s6_addr32[0] ^= 0xffffffff;
	result.m_addr.s6_addr32[1] ^= 0xffffffff;
	result.m_addr.s6_addr32[2] ^= 0xffffffff;
	result.m_addr.s6_addr32[3] ^= 0xffffffff;
#endif
	return result;
}

bool CIPv6AddressImpl::operator == (const CIPv6AddressImpl& addr) const
{
	return m_scope == addr.m_scope && 0 == std::memcmp(&addr.m_addr, &m_addr, sizeof(m_addr));
}

bool CIPv6AddressImpl::operator != (const CIPv6AddressImpl& addr) const
{
	return !(*this == addr);
}

#endif

NET_NAMESPACE_END
